#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "syntax.h"
#include "tools.h"

void syntaxAnalysis(char *file_input, char *file_output) {
    // 打开文件
    FILE *fp_in, *fp_out;
    if ((fp_in = fopen(file_input, "r")) == NULL) {
        printf("File open error!\n");
        exit(-1);
    }
    if ((fp_out = fopen(file_output, "w")) == NULL) {
        printf("File open error!\n");
        exit(-1);
    }

    // 数据结构初始化
    memset(hold, '\0', sizeof(hold));
    num_register = 0;
    top_num = -1;
    top_opt = -1;

    // 逐行读入语法分析
    fscanf(fp_in, "%s", hold);
    while(strlen(hold) != 0) {
        // CompUnit语法判断
        if (isFuncDefBegin(hold)) {
            analyseFuncDef(fp_in, fp_out);
        }

        // 语法错误
        else {
            exit(2);
        }
    }

    // 关闭文件
    fclose(fp_in);
    fclose(fp_out);
}


int isFuncDefBegin(char str[]) {
    return (strcmp(str, "Int") == 0);
}

void analyseFuncDef(FILE *fp_in, FILE *fp_out) {
    // FuncType
    if (strcmp(hold, "Int") == 0) {
        fprintf(fp_out, "define dso_local i32 ");
    } else {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);

    // Ident
    if (strncmp(hold, "Ident", 5) == 0) {
        slice(hold, 6, strlen(hold)-2);
        fprintf(fp_out, "@%s ", hold);
    } else {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);

    // '('
    if (strcmp(hold, "LPar") == 0 ) {
        fprintf(fp_out, "(");
    } else {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);

    // ')'
    if (strcmp(hold, "RPar") == 0 ) {
        fprintf(fp_out, ")");
    } else {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);

    // Block
    analyseBlock(fp_in, fp_out);
}

void analyseBlock(FILE *fp_in, FILE *fp_out) {
    // '{'
    if (strcmp(hold, "LBrace") == 0 ) {
        fprintf(fp_out, "{\n");
    } else {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);
    
    // Stmt
    analyseStmt(fp_in, fp_out);

    // '}'
    if (strcmp(hold, "RBrace") == 0 ) {
        fprintf(fp_out, "}");
    } else {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);
}

void analyseStmt(FILE *fp_in, FILE *fp_out) {
    // return
    if (strcmp(hold, "Return") != 0) {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    // fscanf(fp_in, "%s", hold);

    // Exp
    analyseExp(fp_in, fp_out);
    fprintf(fp_out, "\tret i32 %%x%d\n", num_register-1);

    // ';'
    if (strcmp(hold, "Semicolon") != 0) {
        exit(2);   // 语法错误
    }
    memset(hold, '\0', sizeof(hold));
    fscanf(fp_in, "%s", hold);
}

void analyseExp(FILE *fp_in, FILE *fp_out) {
    // 初始化
    memset(operators, 0, sizeof(operators));
    memset(numbers, 0, sizeof(numbers));
    memset(registers, -1, sizeof(registers));
    operators[++top_opt] = Boundary;
    num_register = 0;
    int flag_read = 1;   // 循环是否需要新读入 
    enum operator opt = Boundary;
    
    // 循环处理
    while (1) {
        // 读入字符
        if (flag_read) {
            fscanf(fp_in, "%s", hold);
            enum operator pre = opt;
            opt = judgeOpt(hold, pre);
            if (pre == Number && (opt == Number || opt == LPar)) {
                printf("wrong priority");
                exit(2);
            }
        }
        // 分析结束
        if (opt == Boundary && operators[top_opt] == Boundary && top_num != -1) {
            printf("over: %d\n", numbers[top_num]);
            break;
        }
        // 优先级等于
        else if (priority[operators[top_opt]][opt] == 0) {
            if (top_num == -1 || operators[top_opt] != LPar) {
                printf("parenthesis error");
                exit(2);    // 语法错误
            }
            top_opt--;
            flag_read = 1;
        }
        // 优先级小于
        else if (priority[operators[top_opt]][opt] == 1) {
            if (opt == Number) {
                if (strncmp(hold, "Dec", 3) == 0) { 
                    slice(hold, 10, strlen(hold)-2); 
                    numbers[++top_num] = D2D(hold); 
                }
                if (strncmp(hold, "Oct", 3) == 0) { 
                    slice(hold, 10, strlen(hold)-2);
                    numbers[++top_num] = O2D(hold); 
                }
                if (strncmp(hold, "Hex", 3) == 0) {
                    slice(hold, 10, strlen(hold)-2); 
                    numbers[++top_num] = H2D(hold); 
                }           
            } else {
                operators[++top_opt] = opt;
            }
            flag_read = 1;
        }
        // 优先级大于
        else if (priority[operators[top_opt]][opt] == 2) {
            specify(fp_out);
            flag_read = 0;
        }
        // 出错
        else if (priority[operators[top_opt]][opt] == 3) {
            printf("wrong priority\n");
            exit(2);    // 语法错误
        }
    }
}

enum operator judgeOpt(char str[], enum operator pre) {
    if (strcmp(str, "Plus") == 0) {
        if (pre == Number || pre == RPar) {
            return Plus;
        } else {
            return UnPlus;
        }
    }
    if (strcmp(str, "Minus") == 0) {
        if (pre == Number || pre == RPar) {
            return Minus;
        } else {
            return UnMinus;
        }
    }
    if (strcmp(str, "Mult") == 0) { return Mult; }
    if (strcmp(str, "Div") == 0) { return Div; }
    if (strcmp(str, "Rem") == 0) { return Rem; }
    if (strcmp(str, "LPar") == 0) { return LPar; }
    if (strcmp(str, "RPar") == 0) { return RPar; }
    if (isNumber(hold)) { return Number; }
    return Boundary;
}


void specify(FILE *fp_out) {
    enum operator opt = operators[top_opt--];

    // 双目运算
    if (opt != UnPlus && opt != UnMinus) {
        // 运算数不够
        if (top_num < 1) {
            printf("nums not enough(bi)\n");
            exit(2);    // 语法错误 
        }
        // 运算
        int b = numbers[top_num--];
        int a = numbers[top_num--];
        if (opt == Plus) { 
            numbers[++top_num] = a + b;
            printLLVM(fp_out, "add", a, b);
        }
        if (opt == Minus) { 
            numbers[++top_num] = a - b; 
            printLLVM(fp_out, "sub", a, b);
        }
        if (opt == Mult) { 
            numbers[++top_num] = a * b; 
            printLLVM(fp_out, "mul", a, b);
        }
        if (opt == Div) { 
            numbers[++top_num] = a / b;
            printLLVM(fp_out, "sdiv", a, b);
        }
        if (opt == Rem) { 
            numbers[++top_num] = a % b; 
            printLLVM(fp_out, "srem", a, b);
        }
    }

    // 单目运算
    else {
        // 运算数不够
        if (top_num == -1) { 
            printf("nums not enough(un)\n"); 
            exit(2);    // 语法错误 
        }
        // 运算
        int a = numbers[top_num--];
        if (opt == UnPlus) { 
            numbers[++top_num] = a;
        }
        if (opt == UnMinus) { 
            numbers[++top_num] = -a;
            fprintf(fp_out, "\t%%x%d = sub i32 0, %d\n", num_register, a);
            registers[top_num] = num_register++;
            registers[top_num+1] = -1; 
        }
    }
}

void printLLVM(FILE *fp_out, char *str, int a, int b) {
    // i & i
    if (registers[top_num] == -1 && registers[top_num+1] == -1) {
        fprintf(fp_out, "\t%%x%d = %s i32 %d, %d\n", num_register, str, a, b);
    }
    // %i & i
    if (registers[top_num] != -1 && registers[top_num+1] == -1) {
        fprintf(fp_out, "\t%%x%d = %s i32 %%x%d, %d\n", num_register, str, registers[top_num], b);
    }
    // i & %i
    if (registers[top_num] == -1 && registers[top_num+1] != -1) {
        fprintf(fp_out, "\t%%x%d = %s i32 %d, %%x%d\n", num_register, str, a, registers[top_num+1]); 
    }
    // %i & %i
    if (registers[top_num] != -1 && registers[top_num+1] != -1) {
        fprintf(fp_out, "\t%%x%d = %s i32 %%x%d, %%x%d\n", num_register, str, registers[top_num], registers[top_num+1]);   
    }
    registers[top_num] =  num_register++;
    registers[top_num+1] = -1;
}